イメージ (Image)

クラス階層

  • System.Object
    • System.MarshalByRefObject
      • System.Drawing.Image
        • System.Drawing.Bitmap
        • System.Drawing.Imaging.Metafile

オブジェクトの生成

メソッド 読み込み元
FromFile(String) ファイル
FromStream(Stream) ストリーム
FromHbitmap(IntPtr) GDIビットマップ ハンドル
メソッド - Image クラス (System.Drawing) | Microsoft Learn

Bitmapのコンストラクタからも生成できます。c# - Difference between Bitmap.FromFile(path) and new Bitmap(path) - Stack Overflow

FromFile()

ファイルからImageを作成できます。ストリームから読み込む必要がないならば、こちらのメソッドを用いた方が高速です。

public static System.Drawing.Image FromFile (
    string filename,
    bool useEmbeddedColorManagement
    );
FromFile(String, Boolean) - Image.FromFile メソッド (System.Drawing) | Microsoft Learn

useEmbeddedColorManagementをtrueとすると、画像ファイルに埋め込まれている色管理情報 (color management information) に従い、色補正 (color correction) が適用されます。この情報には、ICCプロファイル、ガンマ値、色度(しきど) (chromaticity) 情報を含めることができます。

このuseEmbeddedColorManagementを省略すると、falseが指定されます。FromFile - Image.cs

Image image = Image.FromFile("sample.bmp");

Graphics g = e.Graphics;
g.DrawImage(image, 0, 0);

不正な画像ファイルから読み込むと、「メモリが不足しています。」としてOutOfMemoryExceptionが投げられます。

string filename = "sample.jpg";
new FileStream(filename, FileMode.Create);

Image.FromFile(filename); // OutOfMemoryException

FromStream()

ストリームからImageを作成できます。

public static System.Drawing.Image FromStream (
    System.IO.Stream stream,         // 
    bool useEmbeddedColorManagement, // trueならば、ストリームに埋め込まれている色管理情報を使用する
    bool validateImageData           // trueならば、イメージ データを検証する
    );
FromStream(Stream, Boolean, Boolean) - Image.FromStream メソッド (System.Drawing) | Microsoft Learn

useEmbeddedColorManagementvalidateImageDataがないオーバーロードでは、それぞれfalseとtrueが指定されます。validateImageDataをtrueとしたときはGdip.GdipImageForceValidation()が呼ばれ、それがGdip.Okでなければ例外が発生します。FromStream - Image.cs

不正なストリームから読み込むと、「使用されたパラメーターが有効ではありません。」としてArgumentExceptionが投げられます。

Imageが有効な間はストリームを開いたままとする

ストリームからImageを作成したときは、そのImageが有効な間はストリームを閉じてはなりません。閉じてしまうと保存時に「GDI+ で汎用エラーが発生しました。 (A generic error occurred in GDI+.)」としてExternalExceptionが発生します。c# - Image.Save(..) throws a GDI+ exception because the memory stream is closed - Stack Overflow

Image image;
using (FileStream fs = new FileStream("sample1.bmp", FileMode.Open, FileAccess.Read))
{
    image = Image.FromStream(fs);
}

image.Save("sample2.bmp"); // ExternalExceptionが発生

このことはドキュメントに記載されています。

You must keep the stream open for the lifetime of the Image.

FromStream(Stream) - Image.FromStream Method (System.Drawing) | Microsoft Learn

よってストリームをすぐに閉じる必要があるときは、他のImageに複製します。

Image image;
using (FileStream fs = new FileStream("sample1.bmp", FileMode.Open, FileAccess.Read))
using (Image temp = Image.FromStream(fs))
{
    image = new Bitmap(temp);
}

非同期の読み込み

Image image;
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
using (MemoryStream ms = new MemoryStream())
{
    await fs.CopyToAsync(ms);
    using (Image temp = Image.FromStream(ms))
    {
        image = new Bitmap(temp);
    }
}

メタデータの取得

画像のメタデータを取得するだけならば、validateImageDataをfalseとしてイメージ データの検証を省くことで処理時間を短縮できます。 画像ファイルを高速に読み込むには?[2.0のみ、C#、VB] - @IT 遠藤孝信 (2007/05/24) c# - Getting image dimensions without reading the entire file - Stack Overflow

FromHbitmap()

public static System.Drawing.Bitmap FromHbitmap (
    IntPtr hbitmap,
    IntPtr hpalette
    );
Image.FromHbitmap Method (System.Drawing) | Microsoft Learn

GDIパレットへのハンドルを省略すると、IntPtr.Zeroが渡されます。FromHbitmap - Image.cs

このメソッドではGDIビットマップのコピーが作成されるため、新しいビットマップの作成後にコピー元のビットマップを解放できます。FromHbitmap(IntPtr) - Image.FromHbitmap Method (System.Drawing) | Microsoft Learn

Bitmap bitmap = new Bitmap("sample.bmp");
IntPtr hBitmap = bitmap.GetHbitmap();

Bitmap newBitmap = Image.FromHbitmap(hBitmap);
DeleteObject(hBitmap);

このときDeleteObject()は、Win32のDeleteObject()です。

[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject(IntPtr hObject);

読み込み元なし

Imageクラスとしては新規に作成できないため、このクラスを継承したBitmapとして作成します。

Bitmap bitmap = new Bitmap(100, 100);

Graphics g = Graphics.FromImage(bitmap);
g.DrawLine(Pens.Red, 0, 0, 10, 10);

Graphics.FromImage()からGraphicsを取得することで、Imageに描画することもできます。c# - how to draw a line on a image? - Stack Overflow

プロパティ

プロパティ 内容
int Width ピクセル単位の幅
int Height ピクセル単位の高さ
Size Size ピクセル単位のサイズ。WidthとHeightプロパティから取得するのと等しい Size - Image.cs
SizeF PhysicalDimension 画像の幅と高さ。Bitmapならばピクセル単位、Metafileならば0.01mm単位
float HorizontalResolution 水平方向の解像度
float VerticalResolution 垂直方向の解像度
int Flags ピクセル データの属性フラグ。ImageFlags列挙型のビットの合計値
ImageFormat RawFormat ファイル形式
PixelFormat PixelFormat ピクセル形式
ColorPalette Palette 使用されているカラーパレット
int[] PropertyIdList このImageに格納されている、すべてのID
PropertyItem[] PropertyItems このImageに格納されている、すべてのプロパティ
     
プロパティ - Image クラス (System.Drawing) | Microsoft Learn

RawFormat

このプロパティから得られるImageFormatクラスのGuidプロパティは下表の値となっており、先頭から7~8文字だけが異なっています。

形式 プロパティ Guidの値 ※1
Windows BMP format Bmp b96b3cab-0728-11d3-9d7b-0000f81ef32e
  Emf b96b3cac-0728-11d3-9d7b-0000f81ef32e
  Exif b96b3cb2-0728-11d3-9d7b-0000f81ef32e
GIF format Gif b96b3cb0-0728-11d3-9d7b-0000f81ef32e
  Icon b96b3cb5-0728-11d3-9d7b-0000f81ef32e
JPEG format Jpeg b96b3cae-0728-11d3-9d7b-0000f81ef32e
  MemoryBMP b96b3caa-0728-11d3-9d7b-0000f81ef32e
PNG format Png b96b3caf-0728-11d3-9d7b-0000f81ef32e
TIFF format Tiff b96b3cb1-0728-11d3-9d7b-0000f81ef32e
  Wmf b96b3cad-0728-11d3-9d7b-0000f81ef32e
ImageFormat クラス (System.Drawing.Imaging) | Microsoft Learn FormatID Constants | Microsoft Learn ※1 Console.WriteLine(ImageFormat.Bmp.Guid);のようにして取得

PixelFormat

PixelFormat 列挙型
列挙子 10進 16進 内容
DontCare 0 0x000000 No pixel format is specified.
Undefined 0 0x000000 The pixel format is undefined.
Max 15 0x00000F The maximum value for this enumeration.
Indexed 65536 0x010000 The pixel data contains color-indexed values, which means the values are an index to colors in the system color table, as opposed to individual color values.
Gdi 131072 0x020000 The pixel data contains GDI colors.
Format16bppRgb555 135173 0x021005 Specifies that the format is 16 bits per pixel; 5 bits each are used for the red, green, and blue components. The remaining bit is not used.
Format16bppRgb565 135174 0x021006 Specifies that the format is 16 bits per pixel; 5 bits are used for the red component, 6 bits are used for the green component, and 5 bits are used for the blue component.
Format24bppRgb 137224 0x021808 Specifies that the format is 24 bits per pixel; 8 bits each are used for the red, green, and blue components.
Format32bppRgb 139273 0x022009 Specifies that the format is 32 bits per pixel; 8 bits each are used for the red, green, and blue components. The remaining 8 bits are not used.
Format1bppIndexed 196865 0x030101 Specifies that the pixel format is 1 bit per pixel and that it uses indexed color. The color table therefore has two colors in it.
Format4bppIndexed 197634 0x030402 Specifies that the format is 4 bits per pixel, indexed.
Format8bppIndexed 198659 0x030803 Specifies that the format is 8 bits per pixel, indexed. The color table therefore has 256 colors in it.
Alpha 262144 0x040000 The pixel data contains alpha values that are not premultiplied.
Format16bppArgb1555 397319 0x061007 The pixel format is 16 bits per pixel. The color information specifies 32,768 shades of color, of which 5 bits are red, 5 bits are green, 5 bits are blue, and 1 bit is alpha.
PAlpha 524288 0x080000 The pixel format contains premultiplied alpha values.
Format32bppPArgb 925707 0x0E200B Specifies that the format is 32 bits per pixel; 8 bits each are used for the alpha, red, green, and blue components. The red, green, and blue components are premultiplied, according to the alpha component.
Extended 1048576 0x100000 Reserved.
Format16bppGrayScale 1052676 0x101004 The pixel format is 16 bits per pixel. The color information specifies 65536 shades of gray.
Format48bppRgb 1060876 0x10300C Specifies that the format is 48 bits per pixel; 16 bits each are used for the red, green, and blue components.
Format64bppPArgb 1851406 0x1C400E Specifies that the format is 64 bits per pixel; 16 bits each are used for the alpha, red, green, and blue components. The red, green, and blue components are premultiplied according to the alpha component.
Canonical 2097152 0x200000 The default pixel format of 32 bits per pixel. The format specifies 24-bit color depth and an 8-bit alpha channel.
Format32bppArgb 2498570 0x26200A Specifies that the format is 32 bits per pixel; 8 bits each are used for the alpha, red, green, and blue components.
Format64bppArgb 3424269 0x34400D Specifies that the format is 64 bits per pixel; 16 bits each are used for the alpha, red, green, and blue components.
PixelFormat 列挙型 - PixelFormat 列挙型 (System.Drawing.Imaging) | Microsoft Learn

メソッド

RotateFlip()

イメージを回転、反転、またはそれを同時に行えます。

public void RotateFlip(
    RotateFlipType rotateFlipType
)
Image.RotateFlip メソッド (RotateFlipType) (System.Drawing) | MSDN

一方でイメージの拡大/縮小は、大きさを指定してBitmapのコンストラクタを呼ぶことで行えます。

Save()

イメージを保存できます。

public void Save (string filename);
Save(String) - Image.Save メソッド (System.Drawing) | Microsoft Learn
Image image = Image.FromFile("sample1.bmp");
image.Save("sample2.bmp");

filenameにファイルが存在していた場合には、上書きされます。次のような理由で保存できなかったときは、ExternalExceptionが投げられます。

  • ファイルが存在しており、それがロックされている
  • パスが誤っている
  • イメージ形式が誤っている

Clone()

Imageのコピーを作成できます。

public object Clone ();
Image.Clone メソッド (System.Drawing) | Microsoft Learn

ピクセル データへの参照がコピーされるだけで、それは元のImageと共有されます。 c# - What's the difference between Bitmap.Clone() and new Bitmap(Bitmap)? - Stack Overflow Clone - Image.cs

Clone()で作成したコピーと元のImageに対して異なるスレッドから同時にアクセスされると、0xC0000005 (アクセス違反) や0xc0000374 (ヒープ破損) が発生します。

GetThumbnailImage()

Imageのサムネイルを取得できます。

public System.Drawing.Image GetThumbnailImage (
    int thumbWidth,
    int thumbHeight,
    System.Drawing.Image.GetThumbnailImageAbort callback, // デリゲートの参照を渡す。しかし使用されない
    IntPtr callbackData                                   // IntPtr.Zeroを渡す
    );
Image.GetThumbnailImage メソッド (System.Drawing) | Microsoft Learn

このメソッドは埋め込みサムネイルがあったならばそれが、なければメイン画像が指定のサイズに調整されます。120x120のサイズを指定するのが最適なため、それを超えるならば DrawImage()でサイズを調整します。Remarks - Image.GetThumbnailImage Method (System.Drawing) | Microsoft Learn

サムネイル画像(縮小画像)を作成するには?[C#、VB] - @IT 遠藤孝信 (2007/02/22)

メタデータ

関連するプロパティ
プロパティ 内容
int[] PropertyIdList このImageに格納されている、すべてのID
PropertyItem[] PropertyItems このImageに格納されている、すべてのプロパティ
関連するメソッド
メソッド 機能
SetPropertyItem(PropertyItem) PropertyItemをImageに格納できる
GetPropertyItem(Int32) プロパティのID指定して、プロパティを取得できる
RemovePropertyItem(Int32) プロパティのID指定して、プロパティを除去できる

読み込み

ImageのPropertyItemsプロパティから、Imageに含まれるメタデータを取得できます。その内容はPropertyItemクラスのTypeによって異なるため、その値を元にValueを解釈します。方法 : イメージ メタデータを読み取る - Windows Forms | Microsoft Learn

FileStream fs = new FileStream("sample.jpg", FileMode.Open, FileAccess.Read);
Image image = Image.FromStream(fs, false, false);

const int PropertyTagImageDescription = 0x010E;
int index = Array.IndexOf(image.PropertyIdList, PropertyTagImageDescription);
if (index != -1)
{
    PropertyItem propertyItem = image.PropertyItems[index];
    string text = Encoding.ASCII.GetString(propertyItem.Value);
}

指定のプロパティから取得するにはGetPropertyItem()がありますが、それがサポートされていないと例外が投げられます。

try
{
    const int PropertyTagImageDescription = 0x010E;
    PropertyItem propertyItem = image.GetPropertyItem(PropertyTagImageDescription);

    string text = Encoding.ASCII.GetString(propertyItem.Value);
}
catch (ArgumentException e)
{
}

PropertyItem.Idプロパティの値とその意味は、Property Item Descriptionsで一覧できます。

名前 タグ名 ※1 PropertyItem.Id プロパティの値 データ型 エクスプローラでの表示名
PropertyTagImageDescription ImageDescription 0x010E PropertyTagTypeASCII タイトル (Title)
  XPTitle 0x9C9B PropertyTagTypeByte (Unicode) タイトル (Title)
  XPComment 0x9C9C PropertyTagTypeByte (Unicode) コメント (Comments)
  XPKeywords 0x9C9E PropertyTagTypeByte (Unicode) タグ (Tsgs)
  XPSubject 0x9C9F PropertyTagTypeByte (Unicode) 件名 (Subject)
※1 EXIF Tags gdiplusimaging.h

データ型を表すPropertyItem.Typeは数値で表されますが、その意味は下表の通りであり、その詳細はImage property tag type constantsにあります。

型の種類
データ型
1 PropertyTagTypeByte
2 PropertyTagTypeASCII
3 PropertyTagTypeShort
4 PropertyTagTypeLong
5 PropertyTagTypeRational
7 PropertyTagTypeUndefined
9 PropertyTagTypeSLONG
10 PropertyTagTypeSRational
gdiplusimaging.h

書き込み

Image.SetPropertyItem()でメタデータを書き込めますが、その引数に必要なPropertyItemクラスを生成する手段が提供されていないため、メタデータを有する既存のファイルから取得してくる必要があります。Remarks - Image.SetPropertyItem(PropertyItem) Method (System.Drawing) | Microsoft Learn

Image image = Image.FromFile("sample.jpg");
byte[] bytes = Encoding.ASCII.GetBytes("ABC\0");

const int PropertyTagImageDescription = 0x010E;
PropertyItem propertyItem = image.GetPropertyItem(PropertyTagImageDescription);
propertyItem.Value = bytes;
propertyItem.Len = bytes.Length;

image.SetPropertyItem(propertyItem);

またはGetConstructor()を用いてPropertyItemのインスタンスを生成する方法もあります。

Type type = typeof(PropertyItem);
ConstructorInfo constructorInfo = type.GetConstructor(
    BindingFlags.Instance | BindingFlags.NonPublic,
    null,
    Type.EmptyTypes,
    null);
PropertyItem propertyItem = (PropertyItem)constructorInfo.Invoke(null);


const int PropertyTagImageDescription = 0x010E;
const short PropertyTagTypeASCII = 2;
byte[] bytes = Encoding.ASCII.GetBytes("ABC\0");

propertyItem.Id = PropertyTagImageDescription;
propertyItem.Type = PropertyTagTypeASCII;
propertyItem.Value = bytes;
propertyItem.Len = bytes.Length;

SetPropertyItem()でメタデータを書き込むとき、異なるスレッドからGraphics.DrawImage()などでImageがアクセスされないようにします。さもなくばArgumentException例外が投げられます。

複数のスレッドからのアクセス

同一のImageに対して複数のスレッドからアクセスすると、「オブジェクトは現在他の場所で使用されています。 (Object is currently in use elsewhere.)」としてInvalidOperationExceptionが投げられます。よってそのような場所ではlockで同期させるか、Clone()で複製を作ることで同一のオブジェクトとならないようにします。なおClone()もスレッドセーフではないため、これの呼び出しも同期させる必要があります。c# - InvalidOperationException - object is currently in use elsewhere - Stack Overflow

Microsoft Learnから検索