3

ユーザーが写真をアップロードしてギャラリーを作成できる Web アプリケーションがあります。何年も前にアプリケーションを作成したとき、私はImageMagickを選択し、切り抜きとサイズ変更はすべて ImageMagick で行いました。

アプリケーションをゼロから書き直したので、 ImageMagick をネイティブGDI+操作に置き換えましたが、GDI+ について学べば学ぶほど、間違った選択をしたのではないかと怖くなりました。

GDI+ はデスクトップ用であり、サーバー アプリケーションでは使用しないでください。詳細はわかりませんが、メモリ消費のためだと思います。実際、GDI+ は ImageMagick よりも多くのメモリを使用して、同じ画像に対して同じ操作(トリミングとサイズ変更) を実行していることがわかります (正直に言うと、GDI+ はより速く)。

私は、GDI+、ImageMagick、またはその他のライブラリは、これらの基本的な操作について多かれ少なかれ同じであるべきだと信じていました。また、MS が.NETで出荷しているものは何でも少なくとも問題ないと信じて、ネイティブ GDI+ を使用するというアイデアが好きでした。

使用する適切なアプローチ/ツールは何ですか?

これは私がトリミングするために使用するコードです:

internal Image Crop(Image image, Rectangle r)
{
    Bitmap bmpCrop;
    using (Bitmap bmpImage = new Bitmap(image))
    {
        bmpCrop = bmpImage.Clone(r, bmpImage.PixelFormat);
        bmpImage.Dispose();
    }
    return (Image)(bmpCrop);
}

これは、サイズ変更に使用するコードです。

internal Image ResizeTo(Image sourceImage, int width, int height)
{
    System.Drawing.Image newImage = new Bitmap(width, height);
    using (Graphics gr = Graphics.FromImage(newImage))
    {
        gr.SmoothingMode = SmoothingMode.AntiAlias;
        gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
        gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
        gr.DrawImage(sourceImage, new Rectangle(0, 0, width, height));
        gr.Dispose();
    }
    return newImage;
}
4

2 に答える 2

2

サーバーで GDI+ を使用すべきではないと人々が言っ​​ている場所へのリンクを教えてください。彼らは私が知らない何かを知っているのかもしれません。

GDI+ の仕組みについてはいくつか知っていますが、ImageMagick については何も知りません。ImageMagick のアーキテクチャを説明しているこのページに出くわしました: http://www.imagemagick.org/script/architecture.php

ImageMagick は内部的に画像を 4 チャネルと特定のビット深度 (通常はチャネルあたり 16 ビット) の非圧縮形式に変換し、サイズに応じてメモリ内またはディスク上にある非圧縮データで作業を行うようです。「identify -version」は、ビット深度が何であるかを教えてくれます。私の印象では、32 ビット RGBA を使用する Q8 バージョンを使用しない限り、ImageMagick は通常、内部で 64 ビット RGBA バッファを使用して動作します。複数のスレッドを使用することもできますが、非常に大きな画像を扱っていない限り、それが問題になるとは思えません。(非常に大きな画像を扱っている場合は、ImageMagick が明らかに勝者です。)

GDI+ Bitmap オブジェクトは、常に圧縮されていないデータをメモリに格納し、通常はデフォルトで 32 ビット RGBA になります。それと 32 ビット RGB は、おそらく最も効率的な形式です。GDI+ は描画ライブラリであり、大きな画像用に設計されたものではありませんが、少なくとも Bitmap オブジェクトは、ピクセル データと画像メタデータ用のメモリ以外のリソースを保持しません (一般的な考えに反して、HBITMAP は含まれていません)。オブジェクト)。

だから彼らは私にとても似ているようです。あなたのユースケースでは、一方が他方よりも明らかに優れているとは言えません。imagemagick を使用する場合は、特に精度が重要でない限り、速度とメモリを向上させるために Q8 ビルドを使用する必要があります。

操作がロード、保存、スケーリング、トリミングのみの場合、必要に応じて後で実装を簡単に置き換えることができるはずです。

メタファイルを扱う必要がない限り、画像ではなくビットマップ オブジェクトを内部的に使用する必要があります。そうすれば、Crop 関数で中間の Bitmap オブジェクトを作成する必要がなくなります。その中間オブジェクトは、観察した余分なメモリ消費の一部の背後にある可能性があります。外部ソースから Image オブジェクトを取得する場合は、それらを Bitmap にキャストし、それが機能しない場合は新しい Bitmap を作成することをお勧めします。

また、"using" ステートメントは Dispose を自動的に呼び出すため、明示的に呼び出す必要もありません。

于 2012-06-12T02:20:58.917 に答える
0

私は自分で何かを書きました:

public void ResizeImageAndRatio(string origFileLocation, string newFileLocation, string origFileName, string newFileName, int newWidth, int newHeight, bool resizeIfWider)
{
    System.Drawing.Image initImage = System.Drawing.Image.FromFile(origFileLocation + origFileName);
    int templateWidth = newWidth;
    int templateHeight = newHeight;
    double templateRate = double.Parse(templateWidth.ToString()) / templateHeight;
    double initRate = double.Parse(initImage.Width.ToString()) / initImage.Height;
    if (templateRate == initRate)
    {
        System.Drawing.Image templateImage = new System.Drawing.Bitmap(templateWidth, templateHeight);
        System.Drawing.Graphics templateG = System.Drawing.Graphics.FromImage(templateImage);
        templateG.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
        templateG.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
        templateG.Clear(Color.White);
        templateG.DrawImage(initImage, new System.Drawing.Rectangle(0, 0, templateWidth, templateHeight), new System.Drawing.Rectangle(0, 0, initImage.Width, initImage.Height), System.Drawing.GraphicsUnit.Pixel);
        templateImage.Save(newFileLocation + newFileName, System.Drawing.Imaging.ImageFormat.Jpeg);
    }
    else
    {
        System.Drawing.Image pickedImage = null;
        System.Drawing.Graphics pickedG = null;

        Rectangle fromR = new Rectangle(0, 0, 0, 0);
        Rectangle toR = new Rectangle(0, 0, 0, 0);

        if (templateRate > initRate)
        {
            pickedImage = new System.Drawing.Bitmap(initImage.Width, int.Parse(Math.Floor(initImage.Width / templateRate).ToString()));
            pickedG = System.Drawing.Graphics.FromImage(pickedImage);

            fromR.X = 0;
            fromR.Y = int.Parse(Math.Floor((initImage.Height - initImage.Width / templateRate) / 2).ToString());
            fromR.Width = initImage.Width;
            fromR.Height = int.Parse(Math.Floor(initImage.Width / templateRate).ToString());

            toR.X = 0;
            toR.Y = 0;
            toR.Width = initImage.Width;
            toR.Height = int.Parse(Math.Floor(initImage.Width / templateRate).ToString());
        }
        else
        {
            pickedImage = new System.Drawing.Bitmap(int.Parse(Math.Floor(initImage.Height * templateRate).ToString()), initImage.Height);
            pickedG = System.Drawing.Graphics.FromImage(pickedImage);

            fromR.X = int.Parse(Math.Floor((initImage.Width - initImage.Height * templateRate) / 2).ToString());
            fromR.Y = 0;
            fromR.Width = int.Parse(Math.Floor(initImage.Height * templateRate).ToString());
            fromR.Height = initImage.Height;

            toR.X = 0;
            toR.Y = 0;
            toR.Width = int.Parse(Math.Floor(initImage.Height * templateRate).ToString());
            toR.Height = initImage.Height;
        }

        pickedG.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
        pickedG.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

        pickedG.DrawImage(initImage, toR, fromR, System.Drawing.GraphicsUnit.Pixel);

        System.Drawing.Image templateImage = new System.Drawing.Bitmap(templateWidth, templateHeight);
        System.Drawing.Graphics templateG = System.Drawing.Graphics.FromImage(templateImage);
        templateG.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
        templateG.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
        templateG.Clear(Color.White);
        templateG.DrawImage(pickedImage, new System.Drawing.Rectangle(0, 0, templateWidth, templateHeight), new System.Drawing.Rectangle(0, 0, pickedImage.Width, pickedImage.Height), System.Drawing.GraphicsUnit.Pixel);
        templateImage.Save(newFileLocation + newFileName, System.Drawing.Imaging.ImageFormat.Jpeg);

        templateG.Dispose();
        templateImage.Dispose();

        pickedG.Dispose();
        pickedImage.Dispose();
    }
    initImage.Dispose();
}
于 2012-06-10T12:58:40.443 に答える