.NETアプリケーションを介して消費可能なGPU(グラフィックカード)を使用して画像のサイズを変更する方法はありますか?
画像のサイズを変更するための非常にパフォーマンスの高い方法を探していますが、GPUはCPU(C#を使用するGDI +)よりもはるかに高速に実行できると聞いています。GPUを使用して.NETで使用できる画像のサイズを変更する既知の実装またはサンプルコードはありますか?
XNA を使用して画像のサイズを変更することを考えたことがありますか? ここでは、XNA を使用して画像を png/jpeg として MemoryStream に保存し、後でそれを Bitmap オブジェクトとして再利用する方法を見つけることができます。
編集: XNA をどのように使用できるかについて、ここに例を投稿します (上記のリンクから取得)。
public static Image Texture2Image(Texture2D texture)
{
Image img;
using (MemoryStream MS = new MemoryStream())
{
texture.SaveAsPng(MS, texture.Width, texture.Height);
//Go To the beginning of the stream.
MS.Seek(0, SeekOrigin.Begin);
//Create the image based on the stream.
img = Bitmap.FromStream(MS);
}
return img;
}
また、OpenCV を使用して GPU/マルチコア CPU を使用できることも今日知りました。たとえば、Emguなどの .NET ラッパーの使用を選択し、その Image クラスを使用して画像を操作し、.NET Bitmap クラスを返すことができます。
public static Bitmap ResizeBitmap(Bitmap sourceBM, int width, int height)
{
// Initialize Emgu Image object
Image<Bgr, Byte> img = new Image<Bgr, Byte>(sourceBM);
// Resize using liniear interpolation
img.Resize(width, height, INTER.CV_INTER_LINEAR);
// Return .NET Bitmap object
return img.ToBitmap();
}
GPU を使用しているとは断言できませんが、WPF を使用してパフォーマンスをチェックするための簡単なスパイクを作成しました。
それでも、以下を参照してください。これにより、画像が元のサイズの 33.5 倍 (またはそれ以上) にスケーリングされます。
public void Resize()
{
double scaleFactor = 33.5;
var originalFileStream = System.IO.File.OpenRead(@"D:\SkyDrive\Pictures\Random\Misc\DoIt.jpg");
var originalBitmapDecoder = JpegBitmapDecoder.Create(originalFileStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
BitmapFrame originalBitmapFrame = originalBitmapDecoder.Frames.First();
var originalPixelFormat = originalBitmapFrame.Format;
TransformedBitmap transformedBitmap =
new TransformedBitmap(originalBitmapFrame, new System.Windows.Media.ScaleTransform()
{
ScaleX = scaleFactor,
ScaleY = scaleFactor
});
int stride = ((transformedBitmap.PixelWidth * transformedBitmap.Format.BitsPerPixel) + 7) / 8;
int pixelCount = (stride * (transformedBitmap.PixelHeight - 1)) + stride;
byte[] buffer = new byte[pixelCount];
transformedBitmap.CopyPixels(buffer, stride, 0);
WriteableBitmap transformedWriteableBitmap = new WriteableBitmap(transformedBitmap.PixelWidth, transformedBitmap.PixelHeight, transformedBitmap.DpiX, transformedBitmap.DpiY, transformedBitmap.Format, transformedBitmap.Palette);
transformedWriteableBitmap.WritePixels(new Int32Rect(0, 0, transformedBitmap.PixelWidth, transformedBitmap.PixelHeight), buffer, stride, 0);
BitmapFrame transformedFrame = BitmapFrame.Create(transformedWriteableBitmap);
var jpegEncoder = new JpegBitmapEncoder();
jpegEncoder.Frames.Add(transformedFrame);
using (var outputFileStream = System.IO.File.OpenWrite(@"C:\DATA\Scrap\WPF.jpg"))
{
jpegEncoder.Save(outputFileStream);
}
}
私がテストしていた画像は 495 x 360 でした。保存を含めて、数秒で 16k x 12k 以上にサイズ変更されました。
シングルコアの実行では、1 秒間に約 165 回、1.5 倍にサイズ変更されます。i7 と GPU では何もしていないように見えますが、CPU が 20% の場合、マルチスレッド化すると 5 倍以上になると予想されます。
パフォーマンス プロファイリングは、ネイティブ WPF グラフィックス ライブラリへのホット パスを示し、wpfgfx_v0400.dll
DirectX に近い (Google で「milcore」を参照)。
だから、それは加速するかもしれません、私にはわかりません。
ルーク
はい、GPU を使用して画像のサイズを変更することは可能です。これは、DirectX サーフェスを使用して実行できます (たとえば、C# で SlimDx を使用)。サーフェスを作成してそこに画像を移動する必要があります。その後、GPU のみを使用してこのサーフェスを目的のサイズの別のターゲット サーフェスに引き延ばし、最終的にサイズ変更されたイメージをターゲット サーフェスから取得します。これらのシナリオでは、サーフェスのピクセル形式が異なる可能性があり、GPU が自動的に処理します。ただし、この操作のパフォーマンスに影響を与える可能性があるものがあります。GPU と CPU の間でデータを移動するのは、時間のかかるプロセスです。状況に応じてパフォーマンスを向上させ、CPU と GPU メモリ間の余分なデータ転送を回避するためにいくつかの手法を適用できます。