15

簡単な質問がありますが、これまでのところ答えは見つかりませんでした。C#WinRT / WinMDプロジェクトでjpeg画像のサイズを変更し、新しいjpegとして保存する方法を教えてください。

特定のサイトから毎日の画像をダウンロードしてライブタイルに表示するためのWindows8Metroアプリケーションを開発しています。問題は、画像が1024x1024未満で200kB未満である必要があることです。そうでない場合、画像はタイルに表示されません:http: //msdn.microsoft.com/en-us/library/windows/apps/hh465403.aspx

画像が大きくなった場合、ライブタイルに合わせてサイズを変更するにはどうすればよいですか?アスペクト比を維持したまま、width/2やheight/2のような単純なサイズ変更を考えています。

ここでの特定の要件は、コードがWindowsランタイムコンポーネントとして実行される必要があることです。そのため、WriteableBitmapExライブラリはここでは機能しません。通常のWinRTプロジェクトでのみ使用できます。winmdプロジェクトとしてWriteableBitmapExのブランチもありますが、まだ準備が整っていません。

4

6 に答える 6

18

ここから取得したスケーリングとトリミングの方法の例:

async private void BitmapTransformTest()
{
    // hard coded image location
    string filePath = "C:\\Users\\Public\\Pictures\\Sample Pictures\\fantasy-dragons-wallpaper.jpg";

    StorageFile file = await StorageFile.GetFileFromPathAsync(filePath);
    if (file == null)
        return;

    // create a stream from the file and decode the image
    var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);


    // create a new stream and encoder for the new image
    InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream();
    BitmapEncoder enc = await BitmapEncoder.CreateForTranscodingAsync(ras, decoder);

    // convert the entire bitmap to a 100px by 100px bitmap
    enc.BitmapTransform.ScaledHeight = 100;
    enc.BitmapTransform.ScaledWidth = 100;


    BitmapBounds bounds = new BitmapBounds();
    bounds.Height = 50;
    bounds.Width = 50;
    bounds.X = 50;
    bounds.Y = 50;
    enc.BitmapTransform.Bounds = bounds;

    // write out to the stream
    try
    {
        await enc.FlushAsync();
    }
    catch (Exception ex)
    {
        string s = ex.ToString();
    }

    // render the stream to the screen
    BitmapImage bImg = new BitmapImage();
    bImg.SetSource(ras);
    img.Source = bImg; // image element in xaml

}
于 2012-09-10T12:42:26.070 に答える
11

トリミングではなく、画像のサイズを変更するためのより単純なコード。次のコードは、画像のサイズを80x80に変更します

using (var sourceStream = await sourceFile.OpenAsync(FileAccessMode.Read))
{
    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(sourceStream);
    BitmapTransform transform = new BitmapTransform() { ScaledHeight = 80, ScaledWidth = 80 };
    PixelDataProvider pixelData = await decoder.GetPixelDataAsync(
        BitmapPixelFormat.Rgba8,
        BitmapAlphaMode.Straight,
        transform,
        ExifOrientationMode.RespectExifOrientation,
        ColorManagementMode.DoNotColorManage);

    using (var destinationStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
    {
        BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, destinationStream);
        encoder.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied, 80, 80, 96, 96, pixelData.DetachPixelData());                        
        await encoder.FlushAsync();
    }
}

ソース

于 2012-12-08T08:30:46.110 に答える
7

だからここに私がたくさんのグーグルと試行/エラーコーディングの後に来た私の解決策があります:

ここでの目標は、WinRT、特にバックグラウンドタスクで画像を操作する方法を見つけることでした。バックグラウンドタスクは、 Windowsランタイムコンポーネントタイプである必要があるため、通常のWinRTプロジェクトよりもさらに制限されます。WinRTをターゲットとするNuGetで使用可能なライブラリの99%は、デフォルトのWinRTプロジェクトのみをターゲットにしているため、Windowsランタイムコンポーネントプロジェクトでは使用できません。

最初は、よく知られているWriteableBitmapExライブラリを使用しようとしました。必要なコードをwinmdプロジェクトに移植しました。winmdを対象としたWBEプロジェクトのブランチもありますが、未完成です。[ReadOnlyArray]、[WriteOnlyArray]属性を配列型のメソッドパラメーターに追加した後、およびプロジェクトの名前空間を「Windows」で始まらないものに変更した後、コンパイルしました-winmdプロジェクトの制限。

バックグラウンドタスクプロジェクトでこのライブラリを使用できたとしても、機能していませんでした。これは、私が発見したように、WriteableBitmapをUIスレッドでインスタンス化する必要があり、バックグラウンドタスクで私が知る限りこれは不可能だからです。

それまでの間、WinRTでの画像操作に関するこのMSDNの記事も見つけました。ほとんどのサンプルはJavaScriptセクションにしか存在しないため、最初にC#に変換する必要がありました。WinRTでの画像操作に関するStackOverflowに関するこの役立つ記事も見つけました。

internal static async Task LoadTileImageInternalAsync(string imagePath)
{
    string tileName = imagePath.GetHashedTileName();
    StorageFile origFile = await ApplicationData.Current.LocalFolder.GetFileAsync(imagePath);

    // open file for the new tile image file
    StorageFile tileFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(tileName, CreationCollisionOption.ReplaceExisting);
    using (IRandomAccessStream tileStream = await tileFile.OpenAsync(FileAccessMode.ReadWrite))
    {
        // get width and height from the original image
        IRandomAccessStreamWithContentType stream = await origFile.OpenReadAsync();
        ImageProperties properties = await origFile.Properties.GetImagePropertiesAsync();
        uint width = properties.Width;
        uint height = properties.Height;

        // get proper decoder for the input file - jpg/png/gif
        BitmapDecoder decoder = await GetProperDecoder(stream, imagePath);
        if (decoder == null) return; // should not happen
        // get byte array of actual decoded image
        PixelDataProvider data = await decoder.GetPixelDataAsync();
        byte[] bytes = data.DetachPixelData();

        // create encoder for saving the tile image
        BitmapPropertySet propertySet = new BitmapPropertySet();
        // create class representing target jpeg quality - a bit obscure, but it works
        BitmapTypedValue qualityValue = new BitmapTypedValue(TargetJpegQuality, PropertyType.Single);
        propertySet.Add("ImageQuality", qualityValue);
        // create the target jpeg decoder
        BitmapEncoder be = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, tileStream, propertySet);
        be.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Straight, width, height, 96.0, 96.0, bytes);

        // crop the image, if it's too big
        if (width > MaxImageWidth || height > MaxImageHeight)
        {
            BitmapBounds bounds = new BitmapBounds();
            if (width > MaxImageWidth)
            {
                bounds.Width = MaxImageWidth;
                bounds.X = (width - MaxImageWidth) / 2;
            }
            else bounds.Width = width;
            if (height > MaxImageHeight)
            {
                bounds.Height = MaxImageHeight;
                bounds.Y = (height - MaxImageHeight) / 2;
            }
            else bounds.Height = height;
            be.BitmapTransform.Bounds = bounds;
        }

        // save the target jpg to the file
        await be.FlushAsync();
    }
}

private static async Task<BitmapDecoder> GetProperDecoder(IRandomAccessStreamWithContentType stream, string imagePath)
{
    string ext = Path.GetExtension(imagePath);
    switch (ext)
    {
        case ".jpg":
        case ".jpeg":
            return await BitmapDecoder.CreateAsync(BitmapDecoder.JpegDecoderId, stream);
        case ".png":
            return await BitmapDecoder.CreateAsync(BitmapDecoder.PngDecoderId, stream);
        case ".gif":
            return await BitmapDecoder.CreateAsync(BitmapDecoder.GifDecoderId, stream);
    }
    return null;
}

このサンプルでは、​​1つのファイルを開き、それをバイト配列にデコードして、異なるサイズ/形式/品質の新しいファイルにエンコードし直します。

その結果、Windowsランタイムコンポーネントクラスでも、WriteableBitmapExライブラリがなくても、完全に機能するイメージ操作が可能になります。

于 2012-09-10T20:10:10.883 に答える
2

これは、ピクセルデータにアクセスするオーバーヘッドのない、さらに短いバージョンです。

using (var sourceFileStream = await sourceFile.OpenAsync(Windows.Storage.FileAccessMode.Read))
using (var destFileStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
{
    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(sourceFileStream);
    BitmapEncoder enc = await BitmapEncoder.CreateForTranscodingAsync(destFileStream, decoder);
    enc.BitmapTransform.ScaledWidth = newWidth;
    enc.BitmapTransform.ScaledHeight = newHeight;
    await enc.FlushAsync();
    await destFileStream.FlushAsync();
}
于 2015-09-29T09:33:07.077 に答える
0

私はこの1時間半を費やしてこれを理解しようとしましたが、JPGであるバイト配列があり、与えられた答えを試しました...それを機能させることができなかったので、新しい答えを出します...うまくいけば、これは他の誰かを助けるでしょう...私はJPGを250/250ピクセルに変換しています

private async Task<BitmapImage> ByteArrayToBitmapImage(byte[] byteArray)
    {
        BitmapImage image = new BitmapImage();
        using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
        {
            using (DataWriter writer = new DataWriter(stream.GetOutputStreamAt(0)))
            {
                writer.WriteBytes((byte[])byteArray);
                writer.StoreAsync().GetResults();
            }
            image.SetSource(stream);
        }
        image.DecodePixelHeight = 250;
        image.DecodePixelWidth = 250;

        return image;            
    }
于 2015-02-04T06:00:10.613 に答える
0

高品質の画像が必要な場合は 、BitmapTransformにInterpolationMode=BitmapInterpolationMode.Fantを追加し ます。これが例です。

`public static async Task ResizeImage(Windows.Storage.StorageFile imgeTOBytes、int maxWidth、int maxHeight){

        using (var sourceStream = await imgeTOBytes.OpenAsync(FileAccessMode.Read))
        {
            BitmapDecoder decoder = await BitmapDecoder.CreateAsync(sourceStream);

            double widthRatio = (double)maxWidth / decoder.OrientedPixelWidth;
            double heightRatio = (double)maxHeight / decoder.OrientedPixelHeight;

            double scaleRatio = Math.Min(widthRatio, heightRatio);
            uint aspectHeight = (uint)Math.Floor((double)decoder.OrientedPixelHeight * scaleRatio);
            uint aspectWidth = (uint)Math.Floor((double)decoder.OrientedPixelWidth * scaleRatio);

            BitmapTransform transform = new BitmapTransform() { InterpolationMode = BitmapInterpolationMode.Fant, ScaledHeight = aspectHeight, ScaledWidth = aspectWidth };
            PixelDataProvider pixelData = await decoder.GetPixelDataAsync(
                BitmapPixelFormat.Rgba8,
                BitmapAlphaMode.Premultiplied,
                transform,
                ExifOrientationMode.RespectExifOrientation,
                ColorManagementMode.DoNotColorManage);

            using (var destinationStream = await imgeTOBytes.OpenAsync(FileAccessMode.ReadWrite))
            {
                BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, destinationStream);
                encoder.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Straight, aspectWidth, aspectHeight, 96, 96, pixelData.DetachPixelData());
                await encoder.FlushAsync();
            }
        }`
于 2017-02-01T10:09:54.330 に答える